#include "gauge.h"
#include <QDebug>
#include <QtMath>

#define RING_RADIUS_SCALE_INSIDE 80
#define RING_RADIUS_SCALE_OUTSIDE 60

Gauge::Gauge(QWidget *parent)
    : QWidget{parent}
{
    // 使自定义控件的样式表生效
    this->setAttribute(Qt::WA_StyledBackground, true);
}

void Gauge::setStartAngle(int startAngle)
{
    if (startAngle != _startAngle) {
        _startAngle = startAngle;
        this->update();
    }
}

void Gauge::setSpanAngle(int spanAngle)
{
    if (spanAngle != _spanAngle) {
        _spanAngle = spanAngle;
        this->update();
    }
}

void Gauge::setRingWidth(int ringWidth)
{
    if (_ringWidth != ringWidth) {
        _ringWidth = ringWidth;
        this->update();
    }
}

void Gauge::setRingColor(QColor color)
{
    if (_ringColor != color) {
        _ringColor = color;
        this->update();
    }
}

void Gauge::setPenCapStyle(Qt::PenCapStyle ringStyle)
{
    if (_ringStyle != ringStyle) {
        _ringStyle = ringStyle;
        this->update();
    }
}

void Gauge::setMinorScale(int minorScale)
{
    if (minorScale > 0 && _minorScale != minorScale) {
        _minorScale = minorScale;
        this->update();
    }
}

void Gauge::setMajorScale(int majorScale)
{
    if (majorScale > 0 && _majorScale != majorScale) {
        _majorScale = majorScale;
        this->update();
    }
}

void Gauge::setScaleColor(QColor scaleColor)
{
    if (_scaleColor != scaleColor) {
        _scaleColor = scaleColor;
        this->update();
    }
}

void Gauge::setScalePosition(ScalePosition scalePosition)
{
    if (_scalePosition != scalePosition) {
        _scalePosition = scalePosition;
        this->update();
    }
}

void Gauge::setMinValue(float minValue)
{
    if (_minValue != minValue) {
        _minValue = minValue;
        this->update();
    }
}

void Gauge::setMaxValue(float maxValue)
{
    if (_maxValue != maxValue) {
        _maxValue = maxValue;
        this->update();
    }
}

void Gauge::setTargetValue(float targetValue)
{
    if (_targetValue != targetValue) {
        if (targetValue < _minValue) {
            _targetValue = _minValue;
        }
        else if (targetValue > _maxValue) {
            _targetValue = _maxValue;
        }
        else {
            _targetValue = targetValue;
        }
        this->update();
    }
}

void Gauge::setPrecision(int precision)
{
    if (_precision != precision) {
        _precision = precision;
        this->update();
    }
}

void Gauge::setScaleValueColor(QColor color)
{
    if (_scaleValueColor != color) {
        _scaleValueColor = color;
        this->update();
    }
}

void Gauge::setPointerStyle(PointerStyle pointerStyle)
{
    if (_pointerStyle != pointerStyle) {
        _pointerStyle = pointerStyle;
        this->update();
    }
}

void Gauge::setPointerColor(QColor color)
{
    if (_pointerColor != color) {
        _pointerColor = color;
        this->update();
    }
}

void Gauge::setCurrentRing(bool flag)
{
    if (_currentRing != flag) {
        _currentRing = flag;
        this->update();
    }
}

void Gauge::setCurrentRingColor(QColor color)
{
    if (_currentRingColor != color) {
        _currentRingColor = color;
        this->update();
    }
}

void Gauge::setTitle(const QString& title)
{
    if (_title != title) {
        _title = title;
        this->update();
    }
}

void Gauge::setUnit(const QString& unit)
{
    if (_unit != unit) {
        _unit = unit;
        this->update();
    }
}

void Gauge::setTitleColor(QColor color)
{
    if (_titleColor != color) {
        _titleColor = color;
        this->update();
    }
}

void Gauge::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

    // 移动painter到gauge中心
    painter.translate(this->width() / 2, this->height() / 2);

    // 缩放 保证圆环的宽度 刻度线 刻度值随着仪表盘缩放而缩放
    qreal scaleFactor = qMin(this->width(), this->height()) / 200;
    painter.scale(scaleFactor, scaleFactor);

    drawRing(painter, _startAngle, _spanAngle, _ringColor);
    if (_currentRing) {
        drawRing(painter, _startAngle, _spanAngle * (_targetValue - _minValue) / (_maxValue - _minValue), _currentRingColor);
    }
    drawScale(painter);
    drawScaleValue(painter);
    drawPointer(painter);
    drawTitle(painter);
}

void Gauge::drawRing(QPainter& painter, int startAngle, int spanAngle, QColor color)
{
    //int radius = RING_RADIUS_SCALE_INSIDE;
    int radius = 0;
    switch(_scalePosition) {
    case INSIDE:
    case ON:
        radius = RING_RADIUS_SCALE_INSIDE;
        break;
    case OUTSIDE:
    case OUTSIDE_GAP:
        radius = RING_RADIUS_SCALE_OUTSIDE;
        break;
    default:
        break;
    }
    // 保存painter的状态
    painter.save();

    // 设置画笔
    QPen pen;
    pen.setCapStyle(_ringStyle);  //设置端点样式
    pen.setWidth(_ringWidth);
    pen.setColor(color);
    painter.setPen(pen);

    // 绘制圆环
    QRectF rect = QRectF(-radius, -radius, radius * 2, radius * 2);
    painter.drawArc(rect, startAngle * 16, -spanAngle * 16);

    // 恢复画笔状态
    painter.restore();
}

void Gauge::drawScale(QPainter& painter)
{
    painter.save();

    painter.rotate(-_startAngle);
    int steps = _minorScale * _majorScale;
    float rotateAngle = _spanAngle / (float)steps;

    QPen pen;
    pen.setCapStyle(Qt::FlatCap);
    pen.setColor(_scaleColor);
    for (int i = 0; i <= steps; ++i) {
        int x = 0;
        if (i % _minorScale == 0) {
            pen.setWidthF(1.0);
            painter.setPen(pen);
            switch (_scalePosition) {
            case INSIDE:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_INSIDE - _ringWidth / 2 - 1 : RING_RADIUS_SCALE_INSIDE - _ringWidth / 2;
                painter.drawLine(x, 0, x - 4, 0);
                break;
            case OUTSIDE:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 : RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 + 1;
                painter.drawLine(x, 0, x + 4, 0);
                break;
            case ON:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_INSIDE + _ringWidth / 2 - 1 : RING_RADIUS_SCALE_INSIDE + _ringWidth / 2;
                painter.drawLine(x, 0, x - 4, 0);
                break;
            case OUTSIDE_GAP:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 - 1 : RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2;
                painter.drawLine(x + 5, 0, x + 9, 0);
            }
        } else {
            pen.setWidthF(0.5);
            painter.setPen(pen);
            switch (_scalePosition) {
            case INSIDE:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_INSIDE - _ringWidth / 2 - 1 : RING_RADIUS_SCALE_INSIDE - _ringWidth / 2;
                painter.drawLine(x, 0, x - 2, 0);
                break;
            case OUTSIDE:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 : RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 + 1;
                painter.drawLine(x, 0, x + 2, 0);
                break;
            case ON:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_INSIDE + _ringWidth / 2 - 1 : RING_RADIUS_SCALE_INSIDE + _ringWidth / 2;
                painter.drawLine(x, 0, x - 2, 0);
                break;
            case OUTSIDE_GAP:
                x = _ringStyle == Qt::RoundCap || _ringStyle == Qt::MPenCapStyle ? RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 - 1 : RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2;
                painter.drawLine(x + 5, 0, x + 7, 0);
            }
        }
        painter.rotate(rotateAngle);
    }

    painter.restore();
}

void Gauge::drawScaleValue(QPainter& painter)
{
    painter.save();
    painter.setFont(QFont("微软雅⿊", 8));
    painter.setPen(_scaleValueColor);

    int radius = 0;
    switch(_scalePosition) {
    case INSIDE:
        radius = RING_RADIUS_SCALE_INSIDE - _ringWidth / 2 - 14;
        break;
    case OUTSIDE:
        radius = RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 + 15;
        break;
    case ON:
        radius = RING_RADIUS_SCALE_INSIDE - _ringWidth / 2 - 10;
        break;
    case OUTSIDE_GAP:
        radius = RING_RADIUS_SCALE_OUTSIDE + _ringWidth / 2 + 20;
        break;
    default:
        break;
    }

    float stepAngle = _spanAngle / (float)_majorScale;

    for (int i = 0; i <= _majorScale; i++) {
        double value = 1.0 * i * (_maxValue - _minValue) / (float)_majorScale + _minValue;
        QString text = QString("%1").arg((float)value, 0, 'f', _precision);
        double textWidth = fontMetrics().horizontalAdvance(text);
        double textHeight = fontMetrics().height();

        int x = 0;
        int y = 0;
        float currentAngle = _startAngle - i * stepAngle;
        // 起始角度在第三象限
        if (_startAngle >= 180 && _startAngle < 270) {
            // 当前角度在第三象限
            if (currentAngle >= 180 && currentAngle < 270) {
                x = -radius * qSin((270 - currentAngle) / 180 * M_PI) - textWidth / 2;
                y = radius * qCos((270 - currentAngle) / 180 * M_PI) + textHeight / 4;
            }
            // 当前角度在第二象限
            else if (currentAngle >= 90 && currentAngle < 180) {
                x = -radius * qCos((180 - currentAngle) / 180 * M_PI) - textWidth / 2;
                y = -radius * qSin((180 - currentAngle) / 180 * M_PI) + textHeight / 4;
            }
            // 当前角度在第一象限
            else if (currentAngle >= 0 && currentAngle < 90) {
                x = radius * qSin((90 - currentAngle) / 180 * M_PI) - textWidth / 2;
                y = -radius * qCos((90 - currentAngle) / 180 * M_PI) + textHeight / 4;
            }
            // 当前角度在第四象限
            else if (currentAngle >= -90 && currentAngle < 0) {
                x = radius * qCos((-currentAngle) / 180 * M_PI) - textWidth / 2;
                y = radius * qSin((-currentAngle) / 180 * M_PI) + textHeight / 4;
            }
        }
        // 起始角度在第二象限
        else if (_startAngle >= 90 && _startAngle < 180) {
            if (currentAngle >= 90 && currentAngle < 180) {
                // 第⼆象限
                x = -radius * qCos((180 - currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = -radius * qSin((180 - currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= 0 && currentAngle < 90) {
                // 第⼀象限
                x = radius * qCos(currentAngle * (M_PI / 180)) - textWidth / 2;
                y = -radius * qSin(currentAngle * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= -90 && currentAngle < 0) {
                // 第四象限
                x = radius * qCos((-currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = radius * qSin((-currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= -180 && currentAngle < -90) {
                // 第三象限
                x = -radius * qCos((180 - (-currentAngle)) * (M_PI / 180)) - textWidth / 2;
                y = radius * qSin((180 - (-currentAngle)) * (M_PI / 180)) + textHeight / 4;
            }
        }
        // 起始角度在第一象限
        else if (_startAngle >= 0 && _startAngle < 90) {
            if (currentAngle >= 0 && currentAngle < 90) {
                // 第⼀象限
                x = radius * qCos(currentAngle * (M_PI / 180)) - textWidth / 2;
                y = -radius * qSin(currentAngle * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= -90 && currentAngle < 0) {
                // 第四象限
                x = radius * qCos((-currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = radius * qSin((-currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= -180 && currentAngle < -90) {
                // 第三象限
                x = radius * -qCos((180 - (-currentAngle)) * (M_PI / 180)) - textWidth / 2;
                y = radius * qSin((180 - (-currentAngle)) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= -270 && currentAngle < -180) {
                // 第⼆象限
                x = -radius * qCos((-currentAngle - 180) * (M_PI / 180)) - textWidth / 2;
                y = -radius * qSin((-currentAngle - 180) * (M_PI / 180)) + textHeight / 4;
            }
        }
        // 起始角度在第四象限
        else if (_startAngle >= 270 && _startAngle < 360) {
            if ( currentAngle >= 270 && currentAngle < 360) {
                // 第四象限
                x = radius * qCos((360 - currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = radius * qSin((360 - currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= 180 && currentAngle < 270) {
                // 第三象限
                x = -radius * qSin((270 - currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = radius * qCos((270 - currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= 90 && currentAngle < 180) {
                // 第⼆象限
                x = radius * -qCos((180 - currentAngle) * (M_PI / 180)) - textWidth / 2;
                y = radius * -qSin((180 - currentAngle) * (M_PI / 180)) + textHeight / 4;
            } else if (currentAngle >= 0 && currentAngle < 90) {
                // 第⼀象限
                x = radius * qCos(currentAngle * (M_PI / 180)) - textWidth / 2;
                y = -radius * qSin(currentAngle * (M_PI / 180)) + textHeight / 4;
            }
        }
        painter.drawText(x, y, text);
    }
    painter.restore();
}

void Gauge::drawPointer(QPainter& painter)
{
    painter.save();

    int radius = RING_RADIUS_SCALE_OUTSIDE - 15;
    painter.setPen(Qt::NoPen);
    painter.setBrush(_pointerColor);
    qreal stepAngle = (double)_spanAngle * (_targetValue - _minValue) / (_maxValue - _minValue);
    painter.rotate(-_startAngle);
    painter.rotate(stepAngle);

    QPolygon polygon;
    switch(_pointerStyle) {
    case PointerStyle::CIRCLE:
        polygon.setPoints(3, radius, 0, 0, -3, 0, 3);
        painter.drawPolygon(polygon);
        painter.drawEllipse(QPoint(0, 0), 5, 5);
        break;
    case PointerStyle::SHARP:
        polygon.setPoints(4, radius, 0, 0, 5, -8, 0, 0, -5);
        painter.drawPolygon(polygon);
        painter.setBrush(QColor(255, 255, 255));
        painter.drawEllipse(QPoint(0, 0), 2, 2);
        break;
    default:
        break;
    }

    painter.restore();
}

void Gauge::drawTitle(QPainter& painter)
{
    painter.save();

    int radius = 100;

    QFont font;
    font.setPixelSize(10);
    font.setBold(true);
    painter.setFont(font);

    painter.rotate(-_startAngle);
    painter.rotate(-(360 - _spanAngle) / 2);
    painter.translate(radius / 2, 0);
    painter.rotate((360 - _spanAngle) / 2);
    painter.rotate(_startAngle);

    QRectF textRect(-radius, -radius / 5, radius * 2, radius / 4.5);
    painter.drawText(textRect, Qt::AlignCenter, _title);

    font.setPixelSize(15);
    font.setBold(true);
    painter.setFont(font);
    QString text = QString("%1").arg((double)_targetValue, 0, 'f', _precision);
    text = QString("%1%2").arg(text, _unit);
    QRectF valueRect(-radius, 0, radius * 2, radius / 4.5);
    painter.drawText(valueRect, Qt::AlignCenter, text);

    painter.restore();
}
